﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace VideoLink.Links
{
    /// <summary>
    /// Представляет подключение к настоящему смартлинку.
    /// Является обёрткой для более низкоуровневой билиотеки.
    /// </summary>
    public class SmartLink : ILink
    {
        #region Private fields

        /// <summary>
        /// Ссылка на экземпляр подгружаемого класса, который будет
        /// осуществлять функции
        /// </summary>
        private object instance;

        /// <summary>
        /// Ссылка на реализацию метода Active.Get подгружаемого класса
        /// </summary>
        private MethodInfo getActive;

        /// <summary>
        /// Ссылка на реализацию метода OpenDevice подгружаемого класса
        /// </summary>
        private MethodInfo openDevice;

        /// <summary>
        /// Ссылка на реализацию метода CloseDevice подгружаемого класса
        /// </summary>
        private MethodInfo closeDevice;

        /// <summary>
        /// Ссылка на реализацию метода SendFile подгружаемого класса
        /// </summary>
        private MethodInfo sendFile;
        
        #endregion

        /// <summary>
        /// Создать новый экземпляр класса, который будет 
        /// использовать в качестве реализации экземпляр типа source
        /// </summary>
        /// <param name="source">
        /// Тип, экземпляр которого способен выполнять 
        /// функции по работе со смартлинком
        /// </param>
        public SmartLink(Type source)
        {
            instance = source.GetConstructor(Type.EmptyTypes)
                .Invoke(new object[0]);

            getActive = source.GetProperty("Active").GetAccessors()[0];
            openDevice = source.GetMethod("OpenDevice");
            closeDevice = source.GetMethod("CloseDevice");
            sendFile = source.GetMethod("SendFile");

            source.GetEvent("SendStatus")
                .AddEventHandler(instance, 
                    new TransieveStatusHandler(SendStatusThrower));
            source.GetEvent("GetStatus")
                .AddEventHandler(instance,
                    new TransieveStatusHandler(GetStatusThrower));
            source.GetEvent("NewFileReceived")
                .AddEventHandler(instance,
                    new NewFileHandler(NewFileReceivedThrower));
        }

        #region ILink Members

        /// <summary>
        /// Определяет, установлено ли соединение.
        /// Если установлено (Active = true), то можно начинать передавать,
        /// иначе (Active = false) доставка сообщений не гарантируется
        /// </summary>
        public bool Active
        {
            get 
            {
                return (bool)getActive.Invoke(instance, new object[0]);
            }
        }

        /// <summary>
        /// Вызывается при изменении состояния процесса передачи файла.
        /// </summary>
        public event TransieveStatusHandler SendStatus;

        /// <summary>
        /// Вызывается при изменении состояния процесса получения файла.
        /// </summary>
        public event TransieveStatusHandler GetStatus;

        /// <summary>
        /// Вызывается при получении нового файла.
        /// На момент вызова данного события файл уже готов к использованию.
        /// </summary>
        public event NewFileHandler NewFileReceived;

        /// <summary>
        /// Открыть подключение.
        /// Необходимо вызывать перед началом передачи.
        /// </summary>
        /// <returns>Код ошибки, возникшей при подключении</returns>
        public int OpenDevice()
        {
            return (int)openDevice.Invoke(instance, new object[0]);
        }

        /// <summary>
        /// Закрыть подключение и освободить все ресурсы.
        /// После вызова этого метода данный канал непригоден для работы.
        /// </summary>
        /// <returns>Код ошибки, возникшей при отключении</returns>
        public int CloseDevice()
        {
            return (int)closeDevice.Invoke(instance, new object[0]);
        }

        /// <summary>
        /// Передать файл
        /// </summary>
        /// <param name="fileName">Имя файла, который необходимо передать</param>
        public void SendFile(string fileName)
        {
            sendFile.Invoke(instance, new object[1] { fileName });
        }

        #endregion

        #region Private methods

        /// <summary>
        /// Липовый обработчик события instance.SendStatus.
        /// Вызывает событие SendStatus
        /// </summary>
        /// <param name="finished">-</param>
        /// <param name="progress">-</param>
        private void SendStatusThrower(int finished, int progress)
        {
            if (SendStatus != null)
                SendStatus(finished, progress);
        }

        /// <summary>
        /// Липовый обработчик события instance.GetStatus.
        /// Вызывает событие GetStatus
        /// </summary>
        /// <param name="finished">-</param>
        /// <param name="progress">-</param>
        private void GetStatusThrower(int finished, int progress)
        {
            if (GetStatus != null)
                GetStatus(finished, progress);
        }

        /// <summary>
        /// Липовый обработчик события instance.NewFileReceived.
        /// Вызывает событие NewFileReceived
        /// </summary>
        /// <param name="fileName">-</param>
        private void NewFileReceivedThrower(string fileName)
        {
            if (NewFileReceived != null)
                NewFileReceived(fileName);
        }
        
        #endregion
    }
}
